/*
 * Copyright 2013-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.tinem.platform.web.gateway.filter;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.tinem.platform.module.pojo.co.GatewayHeadName;
import com.tinem.platform.module.pojo.co.RedisKeyEnum;
import com.tinem.platform.web.gateway.util.GatewayUtil;
import com.tinem.platform.web.gateway.util.MDCUtil;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * GatewayFilter that modifies the respons body.
 */
@Slf4j
@Component
public class CacheResponseGatewayFilterFactory extends
        AbstractGatewayFilterFactory<Object> {
    @Resource
    StringRedisTemplate stringRedisTemplate;

    @Override
    public GatewayFilter apply(Object config) {
        ModifyResponseGatewayFilter gatewayFilter = new ModifyResponseGatewayFilter();
        return gatewayFilter;
    }
    public class ModifyResponseGatewayFilter implements GatewayFilter, Ordered {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            return chain.filter(exchange.mutate().response(decorate(exchange)).build());
        }

        @SuppressWarnings("unchecked")
        ServerHttpResponse decorate(ServerWebExchange exchange) {
            return new ServerHttpResponseDecorator(exchange.getResponse()) {

                @Override
                public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                    this.cacheResponse(exchange);
                    return super.writeWith(body);
                }

                @Override
                public Mono<Void> writeAndFlushWith(
                        Publisher<? extends Publisher<? extends DataBuffer>> body) {
                    this.cacheResponse(exchange);
                    return super.writeAndFlushWith(body);
                }
                private void cacheResponse(ServerWebExchange exchange){

                    MDCUtil.set(exchange);

                    String resTimestamp = exchange.getResponse().getHeaders().getFirst(GatewayHeadName.X_PLATFORM_GATEWAY_RES_TIMESTAMP);
                    String gatewayResCode = exchange.getResponse().getHeaders().getFirst(GatewayHeadName.X_PLATFORM_GATEWAY_RES_CODE);
                    String gatewayResSuccess = exchange.getResponse().getHeaders().getFirst(GatewayHeadName.X_PLATFORM_GATEWAY_RES_SUCCES);
                    String gatewayResMessage = exchange.getResponse().getHeaders().getFirst(GatewayHeadName.X_PLATFORM_GATEWAY_RES_MESSAGE);

                    Map<String,String> req = new Hashtable<>(7,1);
                    req.put(GatewayHeadName.X_PLATFORM_GATEWAY_RES_TIMESTAMP,resTimestamp);
                    req.put(GatewayHeadName.X_PLATFORM_GATEWAY_RES_CODE,gatewayResCode);
                    req.put(GatewayHeadName.X_PLATFORM_GATEWAY_RES_SUCCES,gatewayResSuccess);
                    req.put(GatewayHeadName.X_PLATFORM_GATEWAY_RES_MESSAGE,gatewayResMessage);

                    String resCode = exchange.getResponse().getHeaders().getFirst(GatewayHeadName.X_PLATFORM_RES_CODE);
                    String resSuccess = exchange.getResponse().getHeaders().getFirst(GatewayHeadName.X_PLATFORM_RES_SUCCESS);
                    String resMessage = exchange.getResponse().getHeaders().getFirst(GatewayHeadName.X_PLATFORM_RES_MESSAGE);
                    req.put(GatewayHeadName.X_PLATFORM_RES_CODE, StrUtil.nullToDefault(resCode,""));
                    req.put(GatewayHeadName.X_PLATFORM_RES_SUCCESS,StrUtil.nullToDefault(resSuccess,""));
                    req.put(GatewayHeadName.X_PLATFORM_RES_MESSAGE,StrUtil.nullToDefault(resMessage,""));

                    String gatewayRequestId = GatewayUtil.getReq(exchange);
                    String key = RedisKeyEnum.gateway_req_info.getKey(gatewayRequestId);
                    try {
                        stringRedisTemplate.opsForHash().putAll(key,req);
                    }catch (Exception e){
                        log.error("redis cache error:{},{}", key,JSON.toJSONString(req));
                        log.error("",e);
                    }
                    // ---------设置10分钟后过期 ------
                    stringRedisTemplate.expire(key,5, TimeUnit.MINUTES);

                    stringRedisTemplate.opsForList().rightPush(RedisKeyEnum.gateway_req_info_query.getKey(),gatewayRequestId);
                    log.info("request info to redis query.");
                }
            };
        }

        @Override
        public int getOrder() {
            return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 2;
        }

    }
}
